/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-05-06     Zero-Free    first version
 */

#include <stdint.h>
#include <rtthread.h>
#include <rthw.h>
#include <board.h>
#include <drv_clk.h>

#include <drivers/pm.h>
#ifdef RT_USING_DEVICE
    #include <rtdevice.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

static void uart_console_reconfig(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
}

/**
 * This function will put N32G45X into sleep mode.
 *
 * @param pm pointer to power manage structure
 */
static void n32_sleep(struct rt_pm* pm, uint8_t mode)
{
    switch(mode)
    {
        case PM_SLEEP_MODE_NONE:
            break;

        case PM_SLEEP_MODE_IDLE:
            // __WFI();
            break;

        case PM_SLEEP_MODE_LIGHT:
            if(pm->run_mode == PM_RUN_MODE_LOW_SPEED)
            {
                /* Enter LP SLEEP Mode, Enable low-power regulator */
                PWR_EnterSLEEPMode(0, PWR_STOPENTRY_WFI);
            }
            else
            {
                /* Enter SLEEP Mode, Main regulator is ON */
                PWR_EnterSLEEPMode(1, PWR_STOPENTRY_WFI);
            }

            break;

        case PM_SLEEP_MODE_DEEP:
            /* Enter STOP 2 mode  */
            PWR_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
            /* Re-configure the system clock */
            SystemClock_ReConfig(pm->run_mode);
            break;

        case PM_SLEEP_MODE_STANDBY:
            /* Enter STANDBY mode */
            PWR_EnterStandbyState();
            break;

        case PM_SLEEP_MODE_SHUTDOWN:
            /* Enter SHUTDOWNN mode */
            PWR_EnterStandbyState();//Need to debug by Blues
            break;

        default:
            RT_ASSERT(0);
            break;
    }
}

static uint8_t run_speed[PM_RUN_MODE_MAX][2] =
{
    {96, 0},
    {72, 1},
    {24, 2},
    {8,  3},
};

static void n32_run(struct rt_pm* pm, uint8_t mode)
{
    static uint8_t last_mode;
    static char* run_str[] = PM_RUN_MODE_NAMES;

    if(mode == last_mode)
    { return; }

    last_mode = mode;
    RCC_EnableClockSecuritySystem(DISABLE);

    /* 2. 根据RUN模式切换时钟频率(HSI) */
    switch(mode)
    {
        case PM_RUN_MODE_HIGH_SPEED:
            SetSysClockToPLL(96000000, SYSCLK_PLLSRC_HSI);
            break;

        case PM_RUN_MODE_NORMAL_SPEED:
            SetSysClockToPLL(72000000, SYSCLK_PLLSRC_HSI);
            break;

        case PM_RUN_MODE_MEDIUM_SPEED:
            SetSysClockToPLL(24000000, SYSCLK_PLLSRC_HSI);
            break;

        case PM_RUN_MODE_LOW_SPEED: //HSI
            SetSysClockToHSI();
            break;

        default:
            break;
    }

    RCC_EnableClockSecuritySystem(ENABLE);
    SystemCoreClockUpdate();
    SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    rt_kprintf("switch to %s mode, frequency = %d MHz\n", run_str[mode], run_speed[mode][0]);
}

/**
 * This function initialize the power manager
 */
int drv_pm_hw_init(void)
{
    static const struct rt_pm_ops _ops =
    {
        n32_sleep,
        n32_run,
    };
    rt_uint8_t timer_mask = 0;
    /* Enable Power Clock */
    __HAL_RCC_PWR_CLK_ENABLE();
    /* initialize timer mask */
    timer_mask = 1UL << PM_SLEEP_MODE_DEEP;
    /* initialize system pm module */
    rt_system_pm_init(&_ops, timer_mask, RT_NULL);
    return 0;
}

INIT_BOARD_EXPORT(drv_pm_hw_init);
